// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

using System;
using KnessetCore.Fetchers;
using KnessetCore.Parser;
using KnessetCore.Updaters;
using KnessetCore.Utils;
using log4net;

namespace KnessetCore.Engines
{
	/// <summary>
	/// An engine attaches Fetcher, Parser and Updater to work together.
	/// The fetcher fetches data and feeds the parser with it and finally the resulting values are given to
	/// the updater. An engine can use a timer in order to run in a timely manner.
	/// </summary>
	/// <typeparam name="F">Fetcher's type.</typeparam>
	/// <typeparam name="O">Output of parser.</typeparam>
	public class FetcherParserUpdaterEngine <F, O> : Engine
	{
		/// <summary>
		/// A fetcher object.
		/// </summary>
		private readonly IFetcher<F> fethcer;
		/// <summary>
		/// A parser object.
		/// </summary>
		private readonly IParser<F, O> parser;
		/// <summary>
		/// An updater object.
		/// </summary>
		private readonly IUpdater<O> updater;

		/// <summary>
		/// Used in order to log data.
		/// </summary>
		private readonly ILog logger = LogManager.GetLogger("KnessetCore.Engine"); 


		/// <summary>
		/// Create a new engine.
		/// </summary>
		/// <param name="fethcer">A fetcher object.</param>
		/// <param name="parser">A parser object.</param>
		/// <param name="updater">An updater object.</param>
		public FetcherParserUpdaterEngine(IFetcher<F> fethcer, IParser<F, O> parser, IUpdater<O> updater)
		{
			if (fethcer == null) throw new ArgumentNullException("fetcher");
			if (parser == null) throw new ArgumentNullException("parser");
			if (updater == null) throw new ArgumentNullException("updater");
			this.fethcer = fethcer;
			this.parser = parser;
			this.updater = updater;
		}

		/// <summary>
		/// Run a single engine operation.
		/// </summary>
		/// <exception cref="FetchException">Error while trying to fetch data.</exception>
		/// <exception cref="ParseException">Error while trying to parse data.</exception>
		/// <exception cref="UpdateException">Error while updating data.</exception>
		public override void Start()
		{
			try
			{
				// Do not fetch information on saturday.
				DateTime now = DateTime.Now;
				if ((now.DayOfWeek == DayOfWeek.Friday && now.Hour > 19) || (now.DayOfWeek == DayOfWeek.Saturday && now.Hour < 19))
				{
					logger.Info("Not updating database. It's either Friday or Saturday.");
				} else
				{
					// Fetch data.
					F fetchedData = fethcer.Fetch();
					// Parse it.
					O result = parser.Parse(fetchedData);
					// Update with appropriate values.
					updater.Update(result);	
				}
				
			}
			catch (Exception e)
			{
				// In case we have an inner error print it instead of this error.
				if (e.InnerException != null)
				{
					e = e.InnerException;
				}
				Constants.CriticalErrorLogger.Error(string.Format("Error while running engine: {0}\nStack: {1}", e.Message, e.StackTrace));
			}
		}

		
	}
}